﻿#region Copyright(C) 2018 FI Ltd. All rights reserved.
// ==================================================================================================
// Copyright(C) 2018 FI Ltd. All rights reserved.
//
// SYSTEM NAME      : FI.Net Application Framework
// COMPONENT ID     : ApplicationContext
// COMPONENT DESC   : Session based contextual data sharing.
//
// CREATED DATE/BY  : 18 Aug 2018 / Tian Mi
//
// REVISION HISTORY :
// DATE/BY            SR#/CS/PM#/OTHERS      DESCRIPTION OF CHANGE
// ==================================================================================================
#endregion

using System;
using FI.Core.Properties;
using FI.Core.Utilities;

namespace FI.Core.ApplicationContexts
{
    /// <summary>
    /// Define the context locator.
    /// </summary>
   public abstract  class ContextLocatorBase
   {
       private void EnsureCanWrite(ContextItem contextItem)
       {
           Guard.ArgumentNotNull(contextItem, "contextItem");
           ContextItem existing = this.GetContextItem(contextItem.Key);
           if (null != existing && existing.ReadOnly)
           {
               throw new InvalidOperationException(ResourceUtility.Format(Resources.ExceptionCannotChangeReadonlyContextItem));
           }
       }

       /// <summary>
       /// Get an existing context item by given key.
       /// </summary>
       /// <param name="key">The key of the <see cref="ContextItem"/> to get.</param>
       /// <returns>
       /// The <see cref="ContextItem"/> object to get.
       /// </returns>
       public abstract ContextItem GetContextItem(string key);

       /// <summary>
       ///Set context item inernally.
       /// </summary>
       /// <param name="contextItem">The context item.</param>
       protected abstract void SetContextItemCore(ContextItem contextItem);


       /// <summary>
       /// Add a new context item or use the new context item to override the exiting one.
       /// </summary>
       /// <param name="contextItem">The new <see cref="ContextItem"/> to set.</param>
       public void SetContextItem(ContextItem contextItem)
       {
           this.EnsureCanWrite(contextItem);
           this.SetContextItemCore(contextItem);
       }

       /// <summary>
       /// Get all current context item collection.
       /// </summary>
       /// <returns>
       /// A <see cref="ContextItemCollection"/> containg all of the current context items.
       /// </returns>
       [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
       public abstract ContextItemCollection GetCurrentContext();

       /// <summary>
       /// Clear the current context item collection.
       /// </summary>
       public abstract void Clear();


       /// <summary>
       /// Attach a new context item collection to the current context.
       /// </summary>
       /// <param name="context">The <see cref="ContextItemCollection"/> to attach.</param>
       /// <param name="behavior">The <see cref="ContextAttachBehavior"/>.</param>
       [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists")]
       public void AttachContext(ContextItemCollection context, ContextAttachBehavior behavior)
       {
           Guard.ArgumentNotNull(context, "context");
           if (behavior == ContextAttachBehavior.Clear)
           {
               this.Clear();
           }

           foreach (ContextItem contextItem in context)
           {
               if (null == this.GetContextItem(contextItem.Key))
               {
                   this.SetContextItem(contextItem);
                   continue;
               }

               if (behavior == ContextAttachBehavior.Override)
               {
                   this.SetContextItem(contextItem);
               }
           }
       }


       /// <summary>
       /// Check if the context item of the given key exists.
       /// </summary>
       /// <param name="key">The key of the <see cref="ContextItem"/>.</param>
       /// <returns>
       /// true if the <see cref="ContextItem"/> already exists; otherwise, false.
       /// </returns>
        public abstract bool ContextItemExits(string key);
   }
}
